﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;
using System.Threading;

namespace HamsterEngine.SimulationWindow
{
    public partial class Simulation : Form
    {
        public static List<string> methods = new List<string>();
        public static int waitTime = 800;

        private bool simRunning = false;
        MethodInfo mainMethod;
        object hamsterObject;
        private Simulation(object o, MethodInfo mainMethod)
        {
            this.mainMethod = mainMethod;
            this.hamsterObject = o;
            InitializeComponent();
        }



        public static void Show(string code)
        {
            CompilerResults cr = null;
            object o = null;
            Type t = null;
            Action a = () =>
            {
                cr = GetCompilerResults(code);
                o = cr.CompiledAssembly.CreateInstance("HamsterCodeFile.PrimiHamster");
                t = o.GetType();
            };
            a.BeginInvoke(
                (iar) =>
                {
                    Simulation s = new Simulation(o, t.GetMethod("startMethod", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static));
                    s.ShowDialog();
                }, null);
        }


        private static CompilerResults GetCompilerResults(string rawCode)
        {
            CSharpCodeProvider c = new CSharpCodeProvider();
            CompilerParameters cp = new CompilerParameters();
            cp.ReferencedAssemblies.Add("system.dll");
            cp.ReferencedAssemblies.Add("system.xml.dll");
            cp.ReferencedAssemblies.Add("system.data.dll");
            cp.ReferencedAssemblies.Add("HamsterEngine.dll");
            cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");

            cp.CompilerOptions = "/t:library";
            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;
            cp.IncludeDebugInformation = false;

            string sb = GetCode(rawCode);

            CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString());
            return cr;
        }

        private static Type GetType(CompilerResults cr)
        {
            foreach (CompilerError ce in cr.Errors)
            {
                return null;
            }
            Assembly a = cr.CompiledAssembly;
            object o = a.CreateInstance("HamsterCodeFile.PrimiHamster");
            Type t = o.GetType();
            return t;
        }

        private static string GetCode(string rawCode)
        {
            StringBuilder sb = new StringBuilder("");
            sb.Append("using System;\n");
            sb.Append("using System.Windows.Forms;\n");
            sb.Append("using HamsterEngine.SimElements;\n");
            sb.Append("using HamsterEngine;\n");
            sb.Append("using System.Threading;");
            sb.Append("namespace HamsterCodeFile{\n");
            sb.Append("public class PrimiHamster : Hamster\n{\n");
            sb.Append("public void startMethod(){\n");
            sb.Append("try{\n");
            sb.Append("Main();\n");
            sb.Append("}catch(Exception ex){MessageBox.Show(ex.ToString());}\n}"); //vielleicht alle Ausnahmen um sicherzustellen, dass keiner einfach 1/0 eintippt
            sb.Append(rawCode);
            sb.Append("}\n");
            sb.Append("}\n");

            return sb.ToString();
        }

        private void playButton_Click(object sender, EventArgs e)
        {
            try
            {

                try
                {
                    Thread th = new Thread(startSim);
                    th.SetApartmentState(ApartmentState.STA);
                    th.IsBackground = true;
                    th.Start();
                    simRunning = true;
                    simTimer.Enabled = true;
                }
                catch (HamsterException ex)
                {
                    MessageBox.Show(ex.ToString(), "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                this.Refresh();
            }
            catch (HamsterException he)
            {
                MessageBox.Show(he.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void startSim()
        {
            mainMethod.Invoke(hamsterObject, null);
            simRunning = false;
        }

        private void simTimer_Tick(object sender, EventArgs e)
        {
            if (simRunning)
                this.Refresh();
            else
                simTimer.Enabled = false;
        }

        private void hamsterRotateButton_Click(object sender, EventArgs e)
        {
            Territorium.Hamster[0].Orientation++;
            this.Refresh();
        }

        private void speedTrackbar_Scroll(object sender, EventArgs e)
        {
            Simulation.waitTime = 500 - speedTrackbar.Value;
        }

        private void simulationPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Draw(g, new Rectangle(30, -15, simulationPanel.Width, simulationPanel.Height), true);
        }

        public static void Draw(Graphics g, Rectangle bounds, bool drawGrid)
        {
            int breite = bounds.Width / Territorium.Breite, höhe = bounds.Height / Territorium.Höhe;
            try
            {
                for (int i = 0; i < Territorium.Breite; i++)
                {
                    for (int j = 0; j < Territorium.Höhe; j++)
                    {
                        if (drawGrid)
                            g.DrawRectangle(Pens.Black, i * breite + bounds.X, j * höhe + bounds.Y, breite, höhe);
                        SimElements.SimElement s = Territorium.GetField(i, j);
                        if (s == null) continue;
                        g.DrawImage(s.Image, i * breite + bounds.X, j * höhe + bounds.Y, breite, höhe);
                    }
                }
            }
            catch
            {

            }
        }

    }
}
